<template>
    <a-control-group :direction="'v'" :block="true" class="a-input-rich-text">
        <a-control-group
            class="toolbar c-f-shrink-0"
            :class="[
                `c-bg-color-${myColor}`,
                !mySharp ? `c-round-${mySize}` : '',
            ]"
            ref="toolbar"
        >
            <a-dropdown-button-group
                :displayCount="0"
                :trigger="'hover'"
                ref="headerDropdown"
            >
                <a-button
                    :color="myColor"
                    :size="mySize"
                    :disabled="myDisabled"
                    :sharp="i == 1 ? true : mySharp"
                    :square="true"
                    :active="currentFormat.header == i"
                    @click="formatHeader(i)"
                    v-for="i in 6"
                    :key="i"
                    >H{{ i }}</a-button
                >

                <template v-slot:moreBtn>
                    <a-button
                        :color="myColor"
                        :size="mySize"
                        :disabled="myDisabled"
                        :sharp="true"
                        :square="true"
                        :active="!!currentFormat.header"
                        class="with-angle"
                        @click="formatHeader(false)"
                    >
                        <span class="fa fa-fw fa-header"></span>
                        <div
                            class="angle"
                            :class="[`c-bg-color-${myColor}-reverse`]"
                        ></div>
                    </a-button>
                </template>
            </a-dropdown-button-group>
            <a-button
                :color="myColor"
                :size="mySize"
                :disabled="myDisabled"
                :sharp="mySharp"
                :square="true"
                :active="!!currentFormat.bold"
                @click="format('bold')"
                ><span class="fa fa-fw fa-bold"></span
            ></a-button>
            <a-dropdown :trigger="'hover'" ref="sizeDropdown">
                <a-button
                    :color="myColor"
                    :size="mySize"
                    :disabled="myDisabled"
                    :sharp="mySharp"
                    :square="true"
                    :active="!!currentFormat.size"
                    class="with-angle"
                    @click="formatSize(false)"
                >
                    <span class="fa fa-fw fa-text-height"></span>
                    <div
                        class="angle"
                        :class="[`c-bg-color-${myColor}-reverse`]"
                    ></div>
                </a-button>

                <template v-slot:pop>
                    <a-control-group :direction="'v'">
                        <a-button
                            :color="myColor"
                            :size="mySize"
                            :disabled="myDisabled"
                            :sharp="i == 0 ? true : mySharp"
                            :active="currentFormat.size == fontSize.value"
                            @click="formatSize(fontSize.value)"
                            v-for="(fontSize, i) in [
                                {
                                    text: $t('AInputRichText.Small'),
                                    value: 'small',
                                },
                                {
                                    text: $t('AInputRichText.Large'),
                                    value: 'large',
                                },
                                {
                                    text: $t('AInputRichText.Huge'),
                                    value: 'huge',
                                },
                            ]"
                            :key="fontSize.text"
                            >{{ fontSize.text }}</a-button
                        >
                    </a-control-group>
                </template>
            </a-dropdown>
            <a-button
                :color="myColor"
                :size="mySize"
                :disabled="myDisabled"
                :sharp="mySharp"
                :square="true"
                :active="!!currentFormat.italic"
                @click="format('italic')"
                ><span class="fa fa-fw fa-italic"></span
            ></a-button>
            <a-button
                :color="myColor"
                :size="mySize"
                :disabled="myDisabled"
                :sharp="mySharp"
                :square="true"
                :active="!!currentFormat.underline"
                @click="format('underline')"
                ><span class="fa fa-fw fa-underline"></span
            ></a-button>
            <a-button
                :color="myColor"
                :size="mySize"
                :disabled="myDisabled"
                :sharp="mySharp"
                :square="true"
                :active="!!currentFormat.strike"
                @click="format('strike')"
                ><span class="fa fa-fw fa-strikethrough"></span
            ></a-button>
            <a-dropdown :trigger="'hover'" ref="colorDropdown">
                <a-button
                    :color="myColor"
                    :size="mySize"
                    :disabled="myDisabled"
                    :sharp="mySharp"
                    :square="true"
                    :active="!!currentFormat.color"
                    class="with-angle"
                    @click="formatColor(false)"
                >
                    <span>C</span>
                    <div
                        class="angle"
                        :class="[`c-bg-color-${myColor}-reverse`]"
                    ></div>
                </a-button>
                <template v-slot:pop>
                    <div class="color-container" :class="[`c-round-${mySize}`]">
                        <div
                            v-for="(color, i) in [
                                '#000000',
                                '#e60000',
                                '#ff9900',
                                '#ffff00',
                                '#008a00',
                                '#0066cc',
                                '#9933ff',
                                '#ffffff',
                                '#facccc',
                                '#ffebcc',
                                '#ffffcc',
                                '#cce8cc',
                                '#cce0f5',
                                '#ebd6ff',
                                '#bbbbbb',
                                '#f06666',
                                '#ffc266',
                                '#ffff66',
                                '#66b966',
                                '#66a3e0',
                                '#c285ff',
                                '#888888',
                                '#a10000',
                                '#b26b00',
                                '#b2b200',
                                '#006100',
                                '#0047b2',
                                '#6b24b2',
                                '#444444',
                                '#5c0000',
                                '#663d00',
                                '#666600',
                                '#003700',
                                '#002966',
                                '#3d1466',
                            ]"
                            :key="color"
                            class="color-item"
                            :class="[
                                `c-square-${mySize}`,
                                currentFormat.color == color ? 'on' : '',
                            ]"
                            :style="{ background: color }"
                            @click="formatColor(color)"
                        ></div>
                    </div>
                </template>
            </a-dropdown>
            <a-dropdown :trigger="'hover'" ref="backgroundDropdown">
                <a-button
                    :color="myColor"
                    :size="mySize"
                    :disabled="myDisabled"
                    :sharp="mySharp"
                    :square="true"
                    :active="!!currentFormat.background"
                    class="with-angle"
                    @click="formatBackground(false)"
                    ><span>Bg</span>
                    <div
                        class="angle"
                        :class="[`c-bg-color-${myColor}-reverse`]"
                    ></div>
                </a-button>
                <template v-slot:pop>
                    <div class="color-container" :class="[`c-round-${mySize}`]">
                        <div
                            v-for="(background, i) in [
                                '#000000',
                                '#e60000',
                                '#ff9900',
                                '#ffff00',
                                '#008a00',
                                '#0066cc',
                                '#9933ff',
                                '#ffffff',
                                '#facccc',
                                '#ffebcc',
                                '#ffffcc',
                                '#cce8cc',
                                '#cce0f5',
                                '#ebd6ff',
                                '#bbbbbb',
                                '#f06666',
                                '#ffc266',
                                '#ffff66',
                                '#66b966',
                                '#66a3e0',
                                '#c285ff',
                                '#888888',
                                '#a10000',
                                '#b26b00',
                                '#b2b200',
                                '#006100',
                                '#0047b2',
                                '#6b24b2',
                                '#444444',
                                '#5c0000',
                                '#663d00',
                                '#666600',
                                '#003700',
                                '#002966',
                                '#3d1466',
                            ]"
                            :key="background"
                            class="color-item"
                            :class="[
                                `c-square-${mySize}`,
                                currentFormat.background == background
                                    ? 'on'
                                    : '',
                            ]"
                            :style="{ background: background }"
                            @click="formatBackground(background)"
                        ></div>
                    </div>
                </template>
            </a-dropdown>
            <a-button
                :color="myColor"
                :size="mySize"
                :disabled="myDisabled"
                :sharp="mySharp"
                :square="true"
                @click="openImageSelector"
                ><span class="fa fa-fw fa-image"></span>
                <input
                    type="file"
                    ref="imageInput"
                    @change="inputImage"
                    class="c-hide"
                    accept="image/jpeg, image/bmp, image/png, image/gif"
                />
            </a-button>
            <a-button
                :color="myColor"
                :size="mySize"
                :disabled="myDisabled"
                :sharp="mySharp"
                :square="true"
                :active="!!currentFormat.link"
                @click="showLinkInput"
                ><span class="fa fa-fw fa-chain"></span
            ></a-button>
            <a-button
                :color="myColor"
                :size="mySize"
                :disabled="myDisabled"
                :sharp="mySharp"
                :square="true"
                @click="cancelLink"
                ><span class="fa fa-fw fa-chain-broken"></span
            ></a-button>
            <a-button
                :color="myColor"
                :size="mySize"
                :disabled="myDisabled"
                :sharp="mySharp"
                :square="true"
                :active="currentFormat.list == 'ordered'"
                @click="formatList('ordered')"
                ><span class="fa fa-fw fa-list-ol"></span
            ></a-button>
            <a-button
                :color="myColor"
                :size="mySize"
                :disabled="myDisabled"
                :sharp="mySharp"
                :square="true"
                :active="currentFormat.list == 'bullet'"
                @click="formatList('bullet')"
                ><span class="fa fa-fw fa-list-ul"></span
            ></a-button>
            <a-button
                :color="myColor"
                :size="mySize"
                :disabled="myDisabled"
                :sharp="mySharp"
                :square="true"
                @click="formatIndent('+1')"
                ><span class="fa fa-fw fa-indent"></span
            ></a-button>
            <a-button
                :color="myColor"
                :size="mySize"
                :disabled="myDisabled"
                :sharp="mySharp"
                :square="true"
                @click="formatIndent('-1')"
                ><span class="fa fa-fw fa-outdent"></span
            ></a-button>
            <a-dropdown-button-group
                :displayCount="0"
                :trigger="'hover'"
                ref="alignDropdown"
            >
                <a-button
                    :color="myColor"
                    :size="mySize"
                    :disabled="myDisabled"
                    :sharp="i == 0 ? true : mySharp"
                    :square="true"
                    :active="currentFormat.align == align"
                    @click="formatAlign(align)"
                    v-for="(align, i) in ['center', 'right']"
                    :key="align"
                    ><span
                        class="fa fa-fw"
                        :class="[`fa-align-${align}`]"
                    ></span>
                </a-button>

                <template v-slot:moreBtn>
                    <a-button
                        :color="myColor"
                        :size="mySize"
                        :disabled="myDisabled"
                        :sharp="true"
                        :square="true"
                        :active="!!currentFormat.align"
                        class="with-angle"
                        @click="formatAlign(false)"
                    >
                        <span class="fa fa-fw fa-align-justify"></span>
                        <div
                            class="angle"
                            :class="[`c-bg-color-${myColor}-reverse`]"
                        ></div>
                    </a-button>
                </template>
            </a-dropdown-button-group>
        </a-control-group>
        <div
            class="editor c-b-t-0"
            :class="[
                `color-${myColor}`,
                `size-${mySize}`,
                { sharp: mySharp },
                { disabled: myDisabled },
            ]"
            ref="editor"
        ></div>
    </a-control-group>
</template>

<style lang="scss" scoped>
@import "../scss/vars";

.a-input-rich-text {
    .editor {
        min-height: 400px;
        box-sizing: border-box;
        outline: none;
        transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;

        &.disabled {
            opacity: 0.5;
            cursor: not-allowed;
        }

        @each $colorPackageName, $colorPackageValue in $colorPackages {
            $bgColor: map-get($colorPackageValue, "bgColor");

            &.color-#{$colorPackageName} {
                background-color: $defaultBgColor;
                border: solid 1px $bgColor;

                &:focus:not(.disabled) {
                    border-color: darken($bgColor, 20%);
                    box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075),
                        0 0 8px lighten($bgColor, 10%);
                }
            }
        }

        @each $sizePackageName, $sizePackageValue in $sizePackages {
            &.size-#{$sizePackageName} {
                &:not(.sharp) {
                    border-radius: map-get($sizePackageValue, "borderRadius");
                }
            }
        }
    }
}

.color-container {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    grid-template-rows: repeat(5, 1fr);

    .color-item {
        box-sizing: border-box;
        &:hover,
        &.on {
            border: solid 1px #000;
        }
    }
}

.with-angle {
    position: relative;
    overflow: hidden;
}

.angle {
    position: absolute;
    right: -4px;
    bottom: -4px;
    transform: rotate(45deg);
    width: 8px;
    height: 8px;
}
</style>

<script>
import Quill from 'quill'
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import AButton from './AButton.vue'
import AControlGroup from './AControlGroup.vue'
import ADropdownButtonGroup from './ADropdownButtonGroup.vue'
import inputMixin from "../mixins/inputMixin"
import ADropdown from './ADropdown.vue'

export default {
    name: 'AInputRichText',
    components: { AButton, AControlGroup, ADropdownButtonGroup, ADropdown },
    mixins: [inputMixin],
    props: {
        value: {
            type: String,
            default: null
        },
        placeholder: {
            type: String,
            default: null
        },
        uploadImageFunc: {
            type: Function,
            default: file => 'https://abc.xyz/file/fakeimg.ext'
        },
    },
    data() {
        return {
            currentFormat: {},
            diff: {},
            innerValue: null,
        }
    },
    computed: {
        valueForWatch() {
            return this.value
        }
    },
    watch: {
        valueForWatch(val) {
            if (val != this.innerValue) {
                this.editor.pasteHTML(val)
            }
        },
        diff() {
            this.setDiffResult()
        },
    },
    mounted() {
        this.editor = new Quill(this.$refs.editor, {
            theme: 'snow',
            modules: {
                toolbar: false
            },
            placeholder: this.placeholder,
            readOnly: this.myDisabled
        })
        this.editor.on('editor-change', this.onEditorChange)
        this.editor.on('text-change', this.onTextChange)
        this.editor.pasteHTML(this.value)
    },
    methods: {
        getEditorBodyEl() {
            return this.$refs.editor.children[0]
        },
        onEditorChange() {
            let range = this.editor.getSelection()
            if (range == null) {
                this.currentFormat = {}
                return
            }
            let formatObj = this.editor.getFormat(range.index, range.length)
            this.currentFormat = formatObj ?? {}
        },
        onTextChange() {
            let content = this.getEditorBodyEl().innerHTML
            this.innerValue = content
            this.$emit('update:value', content)
            this.computeDiff()
        },
        computeDiff() {
            let maxLengthRule = this.rules.find(a => a.name == 'maxLength')
            if (!maxLengthRule) {
                return null
            }
            let valueLength = this.getValueToValidate().length
            this.diff = {
                valid: valueLength <= maxLengthRule.value,
                text: `${valueLength}/${maxLengthRule.value}`
            }
        },
        getValueToValidate() {
            return this.editor.getText().replace(/\n$/, '')
        },
        formatHeader(value) {
            this.editor.format('header', value)
            this.$refs.headerDropdown.hidePop()
        },
        format(name) {
            let range = this.editor.getSelection()
            if (range == null) {
                return
            }
            let formatObj = this.editor.getFormat(range.index, range.length)
            if (formatObj[name] === true) {
                this.editor.formatText(range.index, range.length, name, false)
            } else {
                this.editor.formatText(range.index, range.length, name, true)
            }
        },
        formatIndent(value) {
            this.editor.format('indent', value)
        },
        formatSize(value) {
            this.editor.format('size', value)
            this.$refs.sizeDropdown.hidePop()
        },
        formatColor(value) {
            this.editor.format('color', value)
            this.$refs.colorDropdown.hidePop()
        },
        formatBackground(value) {
            this.editor.format('background', value)
            this.$refs.backgroundDropdown.hidePop()
        },
        openImageSelector() {
            let range = this.editor.getSelection()
            if (range == null) {
                return
            }
            this.$refs.imageInput.click()
        },
        async inputImage() {
            let file = this.$refs.imageInput.files[0]
            if (!file) {
                return
            }
            let src = await this.uploadImageFunc(file)
            let range = this.editor.getSelection()
            this.editor.insertEmbed(range?.index ?? 0, 'image', src)
            this.$refs.imageInput.value = ''
        },
        showLinkInput() {
            let name = 'link'
            let range = this.editor.getSelection()
            let formatObj = this.editor.getFormat(range.index, range.length)

            let defaultUrl = formatObj[name]

            let url = prompt('请输入链接', defaultUrl)
            if (url) {
                this.editor.format(name, url)
            } else if (url === '') {
                this.editor.format(name, false)
            } else if (url === null) {
                //prompt被取消，不做处理
            }
        },
        cancelLink() {
            this.editor.format('link', false)
        },
        formatList(value) {
            let name = 'list'
            let range = this.editor.getSelection()
            let formatObj = this.editor.getFormat(range.index, range.length)
            if (formatObj[name] === value) {
                this.editor.format(name, false)
            } else {
                this.editor.format(name, value)
            }
        },
        formatAlign(value) {
            this.editor.format('align', value)
            this.$refs.alignDropdown.hidePop()
        },
        setDiffResult() {
            let diff = this.diff
            this.formItem?.setDiffResult(diff?.valid, diff?.text)
        },
    },
    unmounted() {

    }
}
</script>